#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import re
import socket

from common.common import execute_cmd, execute_cmd_oversize_return_value
from common.const import CMDResult
from common.logger import Logger
from db2.comm.util.common_util import Db2CommonUtil
from db2.comm.db2_cmd import get_lang_value

LOGGER = Logger().get_logger(filename="db2.log")


def check_catalog_node(catalog_ip):
    try:
        address = socket.gethostbyname(socket.gethostname())
    except Exception as ex:
        LOGGER.error(f"Get address error {str(ex)}.")
        return False
    LOGGER.info(f"Get address {address}")
    if catalog_ip != address:
        LOGGER.error(f"{catalog_ip} is not equal local address {address}")
        return False
    return True


def check_log_arch_config(user_name, db_name):
    # 检查是否开启归档日志
    std_out = get_db_cfg(user_name, db_name)
    if not std_out:
        LOGGER.error(f"Query database configuration failed.")
        return False
    return "(LOGARCHMETH1) = DISK" in std_out


def check_delta_backup_config(user_name, db_name):
    # 检查是否开启增量备份
    std_out = get_db_cfg(user_name, db_name)
    if not std_out:
        LOGGER.error(f"Query database configuration failed.")
        return False
    if "(TRACKMOD) = YES" in std_out or "(TRACKMOD) = ON" in std_out:
        return True
    LOGGER.error(f"TRACKMOD of this database is inactive.")
    return False


def get_db_cfg(user_name, db_name):
    # 获取配置文件参数
    Db2CommonUtil.check_os_user_with_ex(user_name)
    Db2CommonUtil.check_injection(db_name)
    encoding = get_lang_value(user_name)
    sh_cmd = f"su - {user_name} -c 'db2 get db cfg for {db_name}'"
    LOGGER.info(sh_cmd)
    return get_execute_cmd_res(sh_cmd, encoding)


def check_database_exists(user_name, db_name):
    # 检查数据库是否存在
    Db2CommonUtil.check_os_user_with_ex(user_name)
    encoding = get_lang_value(user_name)
    sh_cmd = f"su - {user_name} -c 'db2 list  db directory'"
    std_out = get_execute_cmd_res(sh_cmd, encoding)
    LOGGER.info(f"Result of database query: {std_out}")
    if not std_out:
        LOGGER.error("Check database not exists")
        return False
    if (f"Database name                        = {db_name}" in std_out
            or f"数据库别名                      = {db_name}" in std_out):
        return True
    LOGGER.error("Check database not exists")
    return False


def get_execute_cmd_res(sh_cmd, encoding):
    try:
        return_code, std_out, std_err = execute_cmd(sh_cmd, encoding=encoding)
    except Exception as ex:
        LOGGER.error(f"Execute shell cmd exception {str(ex)}.")
        return ""
    if return_code != CMDResult.SUCCESS:
        LOGGER.error(f"Execute shell cmd failed, error: {std_err}.")
        return ""
    return str(std_out)


def is_trans_full_backup(user_name, data_path):
    Db2CommonUtil.check_os_user_with_ex(user_name)
    Db2CommonUtil.check_path_list(data_path)
    encoding = get_lang_value(user_name)
    sh_cmd = f"su - {user_name} -c 'ls {data_path}'"
    std_out = get_execute_cmd_res(sh_cmd, encoding)
    if not std_out:
        return True
    full_dir_list = std_out.split()
    for full_dir in full_dir_list:
        if "full_" not in full_dir:
            continue
        path = os.path.join(data_path, full_dir.strip())
        full_file_cmd = f"su - {user_name} -c 'ls {path}'"
        full_file = get_execute_cmd_res(full_file_cmd, encoding)
        if full_file:
            return False
    return True


def is_complete_copy(user_name, path):
    Db2CommonUtil.check_os_user_with_ex(user_name)
    Db2CommonUtil.check_path_list(path)
    encoding = get_lang_value(user_name)
    sh_cmd = f"su - {user_name} -c 'db2ckbkp -h {path}'"
    std_out = get_execute_cmd_res(sh_cmd, encoding)
    if not std_out:
        return False
    if "Image Verification Complete - successful" in std_out:
        return True
    return False


def check_database_pending_status(user_name, db_name):
    Db2CommonUtil.check_os_user_with_ex(user_name)
    Db2CommonUtil.check_injection(db_name)
    sh_cmd = f"su - {user_name} -c 'db2 get db cfg for {db_name}'"
    encoding = get_lang_value(user_name)
    std_out = get_execute_cmd_res(sh_cmd, encoding)
    ret = False
    if not std_out:
        LOGGER.error(f"Get database configuration failed.")
        return ret
    if re.match(r"[\s|\S]+Backup pending\s*= NO[\s|\S]+", std_out) \
            and re.match(r"[\s|\S]+Rollforward pending\s*= NO[\s|\S]+", std_out) \
            and re.match(r"[\s|\S]+Restore pending\s*= NO[\s|\S]+", std_out):
        ret = True
    elif re.match(r"[\s|\S]+备份暂挂\s*= NO[\s|\S]+", std_out) \
            and re.match(r"[\s|\S]+前滚暂挂\s*= NO[\s|\S]+", std_out) \
            and re.match(r"[\s|\S]+复原暂挂\s*= NO[\s|\S]+", std_out):
        ret = True
    return ret


def get_version(user_name):
    Db2CommonUtil.check_os_user_with_ex(user_name)
    get_version_cmd = f"su - {user_name} -c 'db2level'"
    return_code, std_out, std_err = execute_cmd(get_version_cmd)
    if return_code != CMDResult.SUCCESS.value:
        LOGGER.error(f"execute cmd db2 get version error")
        return ""
    for i in std_out.split('\n'):
        pattern = r".*?\"DB2\s+v([0-9]+(\.[0-9])*)\""
        ret = re.match(pattern, i)
        if ret:
            version = ret.groups()[0]
            return version

    LOGGER.error(f"get db2 version error: can not get 'DB2 Vxx' keyword")
    return ""


def check_database_offline_backup_status(user_name, db_name):
    """检查数据库是否需要离线备份"""
    Db2CommonUtil.check_os_user_with_ex(user_name)
    Db2CommonUtil.check_injection(db_name)
    sh_cmd = f"su - {user_name} -c 'db2 \"rollforward db {db_name} query status\"'"
    encoding = get_lang_value(user_name)
    return_code, std_out, std_err = execute_cmd(sh_cmd, encoding=encoding)
    LOGGER.info(f"Check offline backup status completed, std_out: {std_out}.")
    if not std_out:
        LOGGER.error(f"Check offline backup status of {db_name} failed, error info: {std_err}")
        return False
    if "SQL1116N" in std_out and ("BACKUP PENDING state" in std_out or "BACKUP PENDING 状态" in std_out):
        LOGGER.error(f"{db_name} need offline backup, error info: {std_err}")
        return False
    return True
